package com.semidata.trp.decision.utils;

import io.netty.handler.codec.http.Cookie;
import io.netty.handler.codec.http.CookieDecoder;
import io.netty.handler.codec.http.HttpHeaders;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;

import com.semidata.rtd.core.http.server.HttpSession;
import com.semidata.rtd.core.iplib.Area;
import com.semidata.trp.decision.business.Material;
import com.semidata.trp.decision.business.Position;
import com.semidata.trp.decision.business.Resource;
import com.semidata.trp.decision.business.Strategy;
import com.semidata.trp.decision.filter.TaskFilter;

public class Utils {

    public static boolean areaInSet(Area area, Set<Area> set) {

        if (set == null || set.isEmpty()) // empty set means accept anything
            return true;

        for (Area a : set) {
            if (a.contains(area))
                return true;
        }

        return false;
    }

    public static boolean hourInSet(int hour, Set<Integer> set) {

        if (set == null || set.isEmpty()) // empty set means accept anything
            return true;
        return set.contains(hour);
    }

    public static int getHour(long time) {
        return getTimeField(time, Calendar.HOUR_OF_DAY);
    }

    public static int getTimeField(long time, int field) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(time);
        return c.get(field);
    }

    public static long convertIp(String ip) {
        StringTokenizer st = new StringTokenizer(ip, ".");
        if (st.countTokens() != 4)
            return -1;

        try {
            long[] parts = new long[4];
            parts[0] = Integer.parseInt(st.nextToken());
            parts[1] = Integer.parseInt(st.nextToken());
            parts[2] = Integer.parseInt(st.nextToken());
            parts[3] = Integer.parseInt(st.nextToken());

            if (parts[0] < 0 || parts[0] > 0xFF || parts[1] < 0 || parts[1] > 0xFF || parts[2] < 0 || parts[2] > 0xFF || parts[3] < 0 || parts[3] > 0xFF)
                return -1;

            long ret = 0;
            ret = (parts[3] & 0xFF) | ((parts[2] << 8) & 0xFF00) | ((parts[1] << 16) & 0xFF0000) | ((parts[3] << 24) & 0xFF0000);
            return ret;
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    public static Set<Integer> random(int n, int m) {

        if (n < 0 || m < 0 || n < m)
            return null;

        Map<Integer, Integer> map = new HashMap<>();
        Set<Integer> result = new HashSet<>();

        int total = n;

        while (total + m > n) {
            int i = (int) (Math.random() * total);
            int v;
            if (map.containsKey(i))
                v = map.get(i);
            else
                v = i;
            result.add(v);
            total--;
            map.put(i, total);

        }
        return result;
    }

    public static <T> T weightSelect(Map<T, Integer> map) {

        int total = 0;

        class Pair {
            public int index;
            public T obj;

            public Pair(int index, T obj) {
                this.index = index;
                this.obj = obj;
            }
            public String toString() {
                return "" + index;
            }
        }

        Pair[] array = new Pair[map.size()];

        int i = 0;
        for (Map.Entry<T, Integer> entry : map.entrySet()) {
            total += entry.getValue();
            array[i++] = new Pair(total, entry.getKey());
        }

        int min = 0;
        int max = array.length - 1;

        int n = (int) (Math.random() * total);

        while (min <= max) {
            int mid = (min + max) / 2;

            if (n >= array[mid].index) {
                min = mid + 1;
            } else if (mid - 1 >= 0 && n < array[mid - 1].index) {
                max = mid - 1;
            } else {
                return array[mid].obj;
            }
        }
        return null;
    }
    

    public static boolean isValidIp(String ip) {
        if (ip == null)
            return false;
        String[] tokens = ip.split("\\.");
        if (tokens.length != 4)
            return false;

        for (int i = 0; i < 4; i++) {
            try {
                int n = Integer.parseInt(tokens[i].trim());
                if (n < 0 || n > 255)
                    return false;
            } catch (NumberFormatException e) {
                return false;
            }
        }
        return true;
    }

    
    public static String getIp(HttpSession session) {
        String ip = session.getRequest().headers().get("X-Real-IP");
        if (isValidIp(ip))
            return ip;
        SocketAddress addr = session.getCtx().channel().remoteAddress();
        if (addr instanceof InetSocketAddress) {
            return ((InetSocketAddress) addr).getAddress().getHostAddress();
        }
        return "0.0.0.0";
    }

    public static String getUserId(HttpSession session) {
        final String USERID_KEY = "smid";
        Set<Cookie> cookies;
        String cookieString = session.getRequest().headers().get(HttpHeaders.Names.COOKIE);
        cookies = cookieString == null ? new TreeSet<Cookie>() : CookieDecoder.decode(cookieString);

        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(USERID_KEY))
                return cookie.getValue();
        }
        return "";
    }
    
    public static Strategy getDefaultStrategy(Resource resource, TaskFilter filter, Position position, Map<String,Integer> frequencies) {
    	for(Strategy strategy : resource.getStrategies()) {
    		if (strategy.getPriority() < 0) {
    			Map<Material,Integer> materials = filter.filterMaterial(strategy, frequencies);
    			if (materials.size() >= position.getMax())
    				return strategy;
    		}
    	}
    	return null;
    }
}
